package com.redshark.wmm;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.jjoe64.graphview.GraphViewSeries;
import com.redshark.wmm.Bluetooth.Bluetooth;
import com.redshark.wmm.Bluetooth.BluetoothListener;
import com.redshark.wmm.Bluetooth.SDCPMessage;
import com.redshark.wmm.Bluetooth.ScopeMessage;
import com.redshark.wmm.MeasurementProcessing.BatteryProcessor;
import com.redshark.wmm.MeasurementProcessing.CurrentProcessor;
import com.redshark.wmm.MeasurementProcessing.DiodeProcessor;
import com.redshark.wmm.MeasurementProcessing.FrequencyProcessor;
import com.redshark.wmm.MeasurementProcessing.Measurement;
import com.redshark.wmm.MeasurementProcessing.ResistanceProcessor;
import com.redshark.wmm.MeasurementProcessing.VoltageProcessor;
import com.redshark.wmm.UI.MainActivity;

public class MultimeterFragment extends Fragment {
	Bluetooth bluetooth;

	// Bluetooth Sender
	ExecutorService senderExecutorService;
	BluetoothSender bluetoothSender;

	private int batteryLevel = 0;

	// Requestor
	ScheduledExecutorService requestorExecutorService;

	MeasurementRequestor requestor;
	ScheduledFuture<?> future;

	MeasurementRequestor addtlFreqRequestor;
	ScheduledFuture<?> freqFuture;

	MeasurementRequestor batteryRequestor;
	ScheduledFuture<?> batteryFuture;

	// Listener
	BluetoothListener listener;
	ExecutorService listenerExecutorService;

	private static final String TAG = "MultimeterFragment";

	public static final int SCOPE_FRAGMENT = 0;
	public static final int SINGLE_FRAGMENT = 1;

	public static final int BT_DISCONNECTED = 0;
	public static final int BT_CONNECTING = 1;
	public static final int BT_CONNECTED = 2;
	public static final int BT_DISCONNECTING = 3;

	private static final int NOTIF_BATTERY_ID = 1;
	
	final int REQUEST_ENABLE_BT = 1;

	private int connectionStatus;

	public int getConnectionStatus() {
		return connectionStatus;
	}

	public void onCreate(Bundle savedInstanceState) {
		// Create our MeasurementRequestor Executor
		requestorExecutorService = Executors.newSingleThreadScheduledExecutor();

		// Disable Buttons since we are not connected
		MainActivity activity = (MainActivity) getActivity();
		activity.setButtonsEnabled(false);

		// retain this fragment
		setRetainInstance(true);

		super.onCreate(savedInstanceState);
	}

	// Connect and Disconnect
	public void connect() {
		// Check to see that we are not connected
		if (bluetooth == null || !bluetooth.isConnected()) {

			// Get the adaptor
			BluetoothAdapter bluetoothAdaptor = BluetoothAdapter
					.getDefaultAdapter();

			// If the device doesnt support bluetooth
			if (bluetoothAdaptor == null) {
				AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
						.create();
				alertDialog.setTitle("Bluetooth Device not Detected");
				alertDialog
						.setMessage("Your Android device is not compatible with Bluetooth. Please try again on a different device.");
				alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Continue",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int which) {
								dialog.cancel();
							}
						});
				alertDialog.show();

			} else {

				// If we are disabled.
				if (!bluetoothAdaptor.isEnabled()) {
					Intent enableBtIntent = new Intent(
							BluetoothAdapter.ACTION_REQUEST_ENABLE);
					startActivityForResult(enableBtIntent,
							REQUEST_ENABLE_BT);
				} else {
					Log.v(TAG, "Creating new BT Object.");
					bluetooth = new Bluetooth();
					new BluetoothConnectTask().execute(bluetooth);
				}
			}
		} else {
			Log.v("MultiMeter", "Starting Disconnect Task");
			new BluetoothDisconnectTask().execute(bluetooth);
		}
	}

	private void onConnect() {
		if (bluetooth.isConnected()) {
			listener = new BluetoothListener(bluetooth.getInputStream()) {
				private final int CMD_DC_VOLTAGE = 64;
				private final int CMD_DC_CURRENT = 65;
				private final int CMD_RESISTANCE = 66;
				private final int CMD_FREQUENCY = 67;
				private final int CMD_DIODE = 68;
				private final int CMD_BATTERY = 80;

				VoltageProcessor voltageProcessor;
				CurrentProcessor dcCurrentProcessor;
				ResistanceProcessor resistanceProcessor;
				FrequencyProcessor frequencyProcessor;
				DiodeProcessor diodeProcessor;

				private int lastCMDRecieved;
				@Override
				public void onSDCPMessage(SDCPMessage msg) {
					Measurement m;

					if (diodeProcessor != null
							&& lastCMDRecieved != msg.getCommand()) {
						// We may need to do some cleanup
						diodeProcessor.stopTone();
					}

					switch (msg.getCommand()) {

					case CMD_DC_VOLTAGE:
						if (lastCMDRecieved != CMD_DC_VOLTAGE) {
							voltageProcessor = new VoltageProcessor();
						}
						m = voltageProcessor.displayMeasurement(msg.getData());
						displayMeasurement(m);
						break;

					case CMD_DC_CURRENT:
						if (lastCMDRecieved != CMD_DC_CURRENT) {
							dcCurrentProcessor = new CurrentProcessor();
						}

						m = dcCurrentProcessor
								.displayMeasurement(msg.getData());
						if (m != null) {
							displayMeasurement(m);
						}

						break;

					case CMD_RESISTANCE:
						if (lastCMDRecieved != CMD_RESISTANCE) {
							resistanceProcessor = new ResistanceProcessor();
						}
						m = resistanceProcessor.displayMeasurement(msg
								.getData());
						if (m != null) {
							displayMeasurement(m);
						}

						break;

					case CMD_FREQUENCY:
						// Decide if we need to redo the frequency processor
						if (lastCMDRecieved != CMD_FREQUENCY) {
							frequencyProcessor = new FrequencyProcessor();
						}

						if (requestor.isScope()) {
							m = frequencyProcessor.displayMeasurement(
									msg.getData(), msg.getAck(), msg.isAck(),
									addtlFreqRequestor);
							displayScopeFreq(m);
						} else {
							m = frequencyProcessor.displayMeasurement(
									msg.getData(), msg.getAck(), msg.isAck(),
									requestor);
							displayMeasurement(m);
						}
						break;

					case CMD_DIODE:
						if (lastCMDRecieved != CMD_DIODE) {
							diodeProcessor = new DiodeProcessor();
						}
						m = diodeProcessor.displayMeasurement(msg.getData());
						displayMeasurement(m);

						break;
					case CMD_BATTERY:
						batteryLevel = BatteryProcessor.checkBattery(msg
								.getData());
						getActivity().invalidateOptionsMenu();

						if (batteryLevel < 30) {
							Context context = getActivity()
									.getApplicationContext();

							NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
									context)
									.setSmallIcon(
											R.drawable.ic_stat_great_red_shark)
									.setContentTitle("Multimeter Battery Low")
									.setContentText(
											"Battery Level: " + batteryLevel
													+ "%").setOngoing(true);

							NotificationManager mNotificationManager = (NotificationManager) context
									.getSystemService(Context.NOTIFICATION_SERVICE);
							// mId allows you to update the notification later
							// on.
							mNotificationManager.notify(NOTIF_BATTERY_ID,
									mBuilder.build());
						} else {
							Context context = getActivity()
									.getApplicationContext();
							NotificationManager mNotificationManager = (NotificationManager) context
									.getSystemService(Context.NOTIFICATION_SERVICE);
							mNotificationManager.cancel(NOTIF_BATTERY_ID);

						}

						// TODO: Make popup that asks you to disconnect and
						// switch off before replacing.
						break;

					}
					lastCMDRecieved = msg.getCommand();
				}

				@Override
				public void onScopeMessage(ScopeMessage m) {
					if (m.getCommand() == MeasurementRequestor.CURRENT_SCOPE_LOW
							|| m.getCommand() == MeasurementRequestor.CURRENT_SCOPE_HIGH) {
						// displayMeasurement(measurement);
						displayScopeData(m.getGraphViewSeries());
						displayScopeRMS(new Measurement(m.getRMS(), 3, "mA rms"));
					} else {
						displayScopeData(m.getGraphViewSeries());
						displayScopeRMS(new Measurement(m.getRMS(), 3, "V rms"));
					}
				}

			};

			listenerExecutorService = Executors.newSingleThreadExecutor();
			listenerExecutorService.execute(listener);

			// Create Sender
			senderExecutorService = Executors.newSingleThreadExecutor();

			// Start battery requestor
			batteryRequestor = new MeasurementRequestor(
					MeasurementRequestor.BATTERY, senderExecutorService,
					bluetooth);
			batteryFuture = requestorExecutorService.scheduleWithFixedDelay(
					batteryRequestor, 0, 1, TimeUnit.MINUTES);

		}
	}

	public void displayMeasurement(final Measurement measurement) {
		final MainActivity activity = (MainActivity) getActivity();
		if (activity != null) {
			activity.runOnUiThread(new Runnable() {
				public void run() {
					activity.displayMeasurement(measurement);
				}
			});
		}
	}

	public void displayScopeRMS(final Measurement measurement) {
		final MainActivity activity = (MainActivity) getActivity();
		if (activity != null) {
			activity.runOnUiThread(new Runnable() {
				public void run() {
					activity.displayScopeRMS(measurement);
				}
			});
		}
	}

	public void displayScopeFreq(final Measurement measurement) {
		final MainActivity activity = (MainActivity) getActivity();
		if (activity != null) {
			activity.runOnUiThread(new Runnable() {
				public void run() {
					activity.displayScopeFreq(measurement);
				}
			});
		}
	}

	public void displayScopeData(final GraphViewSeries series) {
		final MainActivity activity = (MainActivity) getActivity();
		if (activity != null) {
			activity.runOnUiThread(new Runnable() {
				public void run() {
					boolean scopeZoomable = true;
					if (requestor.getType() == MeasurementRequestor.CURRENT_SCOPE_HIGH
							|| requestor.getType() == MeasurementRequestor.CURRENT_SCOPE_LOW) {
						scopeZoomable = false;
					}
					activity.displayScopeData(series, scopeZoomable);
				}
			});
		}
	}

	private class BluetoothConnectTask extends
			AsyncTask<Bluetooth, Void, Boolean> {
		@SuppressWarnings("unused")
		ProgressDialog barProgressDialog;

		protected Boolean doInBackground(Bluetooth... bluetooth) {

			try {
				// Connect Bluetooth
				SharedPreferences sharedPref = PreferenceManager
						.getDefaultSharedPreferences(getActivity()
								.getApplicationContext());
				String macAddr = sharedPref.getString("bluetooth_mac", "");
				bluetooth[0].connect(macAddr);

				// Do some post connect things: 1)Listener 2)Sender
				onConnect();

				return true;
			} catch (Exception e) {
				Log.e(TAG, e.getMessage());
				return false;
			}
		}

		protected void onPreExecute() {

			connectionStatus = BT_CONNECTING;
			MainActivity activity = (MainActivity) getActivity();
			activity.setActionBarSubtitle("Connecting");
		}

		protected void onPostExecute(Boolean connectionSuccessful) {
			MainActivity activity = (MainActivity) getActivity();
			if (connectionSuccessful) {
				connectionStatus = BT_CONNECTED;
				activity.setActionBarSubtitle("Connected");
				activity.setButtonsEnabled(true);
			} else {
				activity.setActionBarSubtitle("Connection Failed!");
			}
		}
	}

	private class BluetoothDisconnectTask extends
			AsyncTask<Bluetooth, Void, Boolean> {

		protected void onPreExecute() {
			// Tell the user we are connecting
			MainActivity activity = (MainActivity) getActivity();
			connectionStatus = BT_DISCONNECTING;
			activity.setActionBarSubtitle("Disconnecting...");
			activity.setButtonsEnabled(false);
		}

		protected Boolean doInBackground(Bluetooth... bluetooth) {
			try {
				// Cancel our requestor
				if (future != null && !future.isDone()) {
					future.cancel(false);
					while (!future.isDone())
						;
					Log.d(TAG, "Requestor cancelled");
				}

				// Cancel our freq requestor (if applicable)
				if (freqFuture != null && !freqFuture.isDone()) {
					freqFuture.cancel(false);
					while (!freqFuture.isDone())
						;
					Log.d(TAG, "Frequency cancelled");
				}

				// Cancel our requestor
				if (batteryFuture != null && !batteryFuture.isDone()) {
					batteryFuture.cancel(false);
					while (!batteryFuture.isDone())
						;
					Log.d(TAG, "Battery requestor cancelled");
				}

				// Stop the sender
				Log.d(TAG, "Request Shutdown of SenderExecutor");
				senderExecutorService.shutdown();
				Log.d(TAG, "Sender Executor shutdown requested"
						+ senderExecutorService.toString());

				try {
					if (!senderExecutorService.awaitTermination(5,
							TimeUnit.SECONDS)) {
						Log.d(TAG,
								"Sender Executor did not shut down in a timely fashon. Force ShutdownNow");
						senderExecutorService.shutdownNow();
					}
				} catch (final InterruptedException pCaught) {
					Log.d(TAG, "Sender Executor threw Interrupted exception");
					senderExecutorService.shutdownNow();
					Thread.currentThread().interrupt();
				}
				Log.d(TAG, "Sender Executor Shutdown");

				// Stop the listener
				// listener.shutdown();

				// while(!listener.isShutdownComplete());

				listener.shutdown();
				listenerExecutorService.shutdown();
				Log.d(TAG, "Listener Executor shutdown requested"
						+ listenerExecutorService.toString());

				bluetooth[0].disconnect();
				Log.d(TAG, "Bluetooth Disconnected requested");

				try {
					if (!listenerExecutorService.awaitTermination(5,
							TimeUnit.SECONDS)) {
						Log.d(TAG,
								"Listener Executor did not shut down in a timely fashon. Force ShutdownNow");
						listenerExecutorService.shutdownNow();
					}
				} catch (final InterruptedException pCaught) {
					Log.d(TAG, "Listener Executor threw Interrupted exception");
					listenerExecutorService.shutdownNow();
					Thread.currentThread().interrupt();
				}
				Log.d(TAG, "Shutdown Complete");
				return true;
			} catch (Exception e) {
				return false;
			}
		};

		protected void onPostExecute(Boolean disconnectionSuccessful) {
			MainActivity activity = (MainActivity) getActivity();

			if (disconnectionSuccessful) {
				connectionStatus = BT_DISCONNECTED;
				activity.setActionBarSubtitle("Disconnected");
				activity.setButtonsEnabled(false);

			} else {
				activity.setActionBarSubtitle("Unable to Disconnect. Please restart application.");
			}
		}
	}

	protected void createRequestorwCfg(int measurement, int optCfg,
			TimeUnit units, int time) {
		// If we already have a requestor, cancel it before starting another.
		if (future != null) {
			future.cancel(false);
		}
		if (freqFuture != null) {
			freqFuture.cancel(false);
		}

		requestor = new MeasurementRequestor(measurement, optCfg,
				senderExecutorService, bluetooth);
		future = requestorExecutorService.scheduleWithFixedDelay(requestor, 1,
				time, units);

	}

	public void createRequestor(int measurement, TimeUnit units, int time) {
		createRequestorwCfg(measurement, 0, units, time);
	}

	public void createScopeRequestor(int scopeCommand, int scopeOptCfg,
			int freqCommand, int freqOptCfg, TimeUnit units, int time) {
		// If we already have a requestor, cancel it before starting another.
		if (future != null) {
			future.cancel(false);
			future = null;
		}
		if (freqFuture != null) {
			freqFuture.cancel(false);
			freqFuture = null;
		}
		addtlFreqRequestor = new MeasurementRequestor(freqCommand, freqOptCfg,
				senderExecutorService, bluetooth);
		freqFuture = requestorExecutorService.scheduleWithFixedDelay(
				addtlFreqRequestor, 1, time, units);

		requestor = new MeasurementRequestor(scopeCommand, scopeOptCfg,
				senderExecutorService, bluetooth);
		future = requestorExecutorService.scheduleWithFixedDelay(requestor, 1,
				time, units);

	}

	public void voltageZoomIn() {
		if (requestor.isScope() && requestor.getType() > 71) {
			requestor.setType(requestor.getType() - 1);
		}
	}

	public void voltageZoomOut() {
		if (requestor.isScope() && requestor.getType() < 75) {
			requestor.setType(requestor.getType() + 1);
		}
	}

	public void timeZoomIn() {
		if (requestor.isScope() && requestor.getCfg() < 31) {
			requestor.setCfg(requestor.getCfg() + 1);
		}
	}

	public void timeZoomOut() {
		if (requestor.isScope() && requestor.getCfg() > 0) {
			requestor.setCfg(requestor.getCfg() - 1);
		}
	}

	public int getBatteryLevel() {
		return batteryLevel;
	}
	
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
	    // Check which request we're responding to
	    if (requestCode == REQUEST_ENABLE_BT) {
	        // Make sure the request was successful
	        if (resultCode == Activity.RESULT_OK) {
	        	connect();
	        }
	    }
	}
}
